require "ISUI/ISImage"
require "ISUI/ISPanelJoypad"
require "ISUI/ISLabel"
require "ISUI/ISEquippedItem"

ESR = ESR or {}
ESR.Rankings = ESR.Rankings or {}
ESR.Rankings.Preferences = ESR.Rankings.Preferences or {}
ESR.Rankings.Commands = ESR.Rankings.Commands or {}
ESR.Rankings.Defaults = ESR.Rankings.Defaults or {}

ESR.Rankings.ResponseSuccess = nil;

ESR.Rankings.data = ESR.Rankings.data or {};

ESR.Rankings.MenuConfigs = {}
ESR.Rankings.MenuConfigs.Button = {}
ESR.Rankings.MenuConfigs.Button.WIDTH = 30;
ESR.Rankings.MenuConfigs.Button.HEIGHT = 30;

ESR.Rankings.MenuConfigs.Buttons = {}
ESR.Rankings.MenuConfigs.Buttons.orderIconAscendant = getTexture("media/textures/orderIconAscendant.png");
ESR.Rankings.MenuConfigs.Buttons.orderIconDescendant = getTexture("media/textures/orderIconDescendant.png");

ESR.Rankings.Buttons = {}

ESR.Rankings.textureOff = getTexture("media/textures/Ranking_off.png");
ESR.Rankings.textureOn = getTexture("media/textures/Ranking_on.png");

ESR.Rankings.Admin = ESR.Rankings.Admin or {}
ESR.Rankings.Admin.ListAllPlayers = nil;
ESR.Rankings.Admin.HiddenPlayers = nil;

ESR.Rankings.window = nil;

ExaStockRanking = ISPanel:derive("ExaStockRanking");

function ExaStockRanking:new(x, y, width, height)
    local o = {}
    x = getCore():getScreenWidth() - width - 30;
    y = getCore():getScreenHeight() - 660;
    -- x = math.max(0, math.min(getCore():getScreenWidth() - width - 30, x))
    -- y = math.max(0, math.min(getCore():getScreenHeight() - height - 30, y))

    o = ISPanel:new(x, y, width, height);
    setmetatable(o, self)
    self.__index = self
    o.borderColor = { r = 0.4, g = 0.4, b = 0.4, a = 1 };
    o.backgroundColor = { r = 0, g = 0, b = 0, a = 0.8 };
    o.width = width;
    o.height = height;
    o.moveWithMouse = true;
    o.menuHeight = 60;
    o._isVisible = false

    ExaStockRanking.instance = o;
    return o;
end

function ExaStockRanking:createChildren()

    self.scoreboardBuys = self:createScoreboard("buys");
    self.scoreboardSells = self:createScoreboard("sells");
    self.scoreboardFund = self:createScoreboard("fund");

    self:addChild(self.scoreboardBuys);
    self:addChild(self.scoreboardSells);
    self:addChild(self.scoreboardFund);

    self.menuButtons = ExaStockRankingMenu:new(0, 0, self:getWidth(), self.menuHeight);
    self:addChild(self.menuButtons);
    self.menuButtons:createButton(self.scoreboardBuys);
    self.menuButtons:createButton(self.scoreboardSells);
    self.menuButtons:createButton(self.scoreboardFund);
    self.menuButtons:pack();
end

function ExaStockRanking:pack()
    self:setWidth(self.menuButtons:getWidth());
    self:setX(getCore():getScreenWidth() - self:getWidth() - 30);

    self.scoreboardBuys:setWidth(self:getWidth());
    self.scoreboardSells:setWidth(self:getWidth());
    self.scoreboardFund:setWidth(self:getWidth());
end

function ExaStockRanking:createScoreboard(name)
    local yAdjust = 3
    local scoreboard = ESRRankingScoreboardItemListBox:new(0, self.menuHeight + yAdjust, self:getWidth(), self:getHeight() - self.menuHeight - (yAdjust * 2));
    scoreboard:initialise();
    scoreboard:instantiate();
    scoreboard:setVisible(false);
    scoreboard.name = name;
    return scoreboard;
end

function ESR.Rankings.showWindow(player, x, y)
    if ESR.Rankings.window then
        ESR.Rankings.window:setVisible(true)
        ESR.Rankings.toolbarButton:setImage(ESR.Rankings.textureOn)
        ExaStockRanking.instance._isVisible = true
        return
    end

    local scoreboard = ExaStockRanking:new(0, 0, 200, 325)
    scoreboard:initialise()
    scoreboard:instantiate()
    scoreboard:pack()
    ESR.Rankings.scoreboard = scoreboard

    local window = scoreboard:wrapInCollapsableWindow(getText("UI_GSBQoL_Rankings_title"), true)
    window.close = ESR.Rankings.hideWindow
    window.closeButton.onmousedown = function(self)
        ExaStockRanking.instance._isVisible = false
        ESR.Rankings.hideWindow(self)
    end
    window:setResizable(false)
    window:addToUIManager()
    x = x or ESR.Rankings.Preferences.x
    y = y or ESR.Rankings.Preferences.y

    if x ~= nil and y ~= nil then
        window:setX(x)
        window:setY(y)
    end

    if isAdmin() or string.lower(player:getAccessLevel()) == "admin" then
        --local th = window:titleBarHeight() - 5
        --local gearButton = ISButton:new(window:getWidth() - 30, 1 + (window:titleBarHeight() - th) / 2, th, th, "", window, ISGSBQoLRankingAdminWindow.OnOpenPanel)
        --gearButton:initialise()
        --gearButton.anchorRight = true
        --gearButton.anchorLeft = false
        --gearButton.borderColor.a = 0.0
        --gearButton.backgroundColor.a = 0
        --gearButton.backgroundColorMouseOver.a = 0
        --gearButton:setImage(getTexture("media/ui/Panel_Icon_Gear.png"))
        --gearButton:setUIName(ISChat.gearButtonName)
        --window:addChild(gearButton)
        --gearButton:setVisible(true)
    end

    ESR.Rankings.window = window;

    ExaStockRanking.instance._isVisible = true
    ESR.Rankings.toolbarButton:setImage(ESR.Rankings.textureOn)
    ESR.Rankings.scoreboard:updateDataScoreboards()

    ESR.Rankings.scoreboardOpened()
end

-- 发送打开排行榜的信息
function ESR.Rankings.scoreboardOpened()
    local player = getPlayer()
    sendClientCommand(player, ESR.Rankings.Shared.MODULE_NAME, "ScoreboardOpened", { player:getUsername() })
end

function ESR.Rankings.hideWindow(self)
    if ESR.Rankings.window and ESR.Rankings.window:getIsVisible() then
        ISCollapsableWindow.close(self);
        ESR.Rankings.toolbarButton:setImage(ESR.Rankings.textureOff);
    end
end

function ESR.Rankings.showWindowToolbar()
    if ESR.Rankings.window and ESR.Rankings.window:getIsVisible() then
        ESR.Rankings.window:close();
        ExaStockRanking.instance._isVisible = false
    else
        ESR.Rankings.showWindow(getPlayer());
    end
end

local prerenderEnabled = false
local enterTime = nil
function ESR.Rankings.OnGameStart()
    enterTime = getTimestamp()
    Events.OnPlayerUpdate.Add(ESR.Rankings.WaitingLoginPreferences)

    ESR.Rankings.Defaults.ISEquippedItem_prerender = ISEquippedItem.prerender
    ISEquippedItem.prerender = function(self)
        ESR.Rankings.Defaults.ISEquippedItem_prerender(self)
        if prerenderEnabled then
            ESR.Rankings.AdjustToolbarButton()
        end
    end
end
-- 登录后等待一段时间，然后从服务器获取玩家偏好设置的函数
function ESR.Rankings.WaitingLoginPreferences()
    if getTimestamp() - enterTime > 2 then
        Events.OnPlayerUpdate.Remove(ESR.Rankings.WaitingLoginPreferences)

        local player = getPlayer()
        sendClientCommand(player, ESR.Rankings.Shared.MODULE_NAME, "RetrievePlayerPreferences", { player:getUsername() })
    end
end
-- 保存玩家窗口偏好设置
function ESR.Rankings.SavePreferences()
    if ESR.Rankings.window then
        local window = ESR.Rankings.window
        local player = getPlayer()
        local preferences = { player:getUsername(), ExaStockRanking.instance._isVisible, window.x, window.y }
        sendClientCommand(player, ESR.Rankings.Shared.MODULE_NAME, "SavePlayerPreferences", preferences)
    else
        print('No preferences to save')
    end
end
-- 调整工具栏按钮位置
function ESR.Rankings.AdjustToolbarButton()
    local y = ISEquippedItem.instance.movableBtn:getY() + ISEquippedItem.instance.movableBtn:getHeight() + 340
    local safetyUI = getPlayerSafetyUI(getPlayer():getPlayerNum())
    if safetyUI then
        y = safetyUI:getY() + safetyUI:getHeight()
        ESR.Rankings.toolbarButton:setY(y)
    end
end

function ESR.Rankings.OnCreatePlayer()
    local y = ISEquippedItem.instance.movableBtn:getY() + ISEquippedItem.instance.movableBtn:getHeight() + 340
    ESR.Rankings.toolbarButton = ISButton:new(0, y, 50, 50, "", nil, ESR.Rankings.showWindowToolbar);
    ESR.Rankings.toolbarButton:setImage(ESR.Rankings.textureOff);
    ESR.Rankings.toolbarButton:setDisplayBackground(false);
    ESR.Rankings.toolbarButton.borderColor = { r = 1, g = 1, b = 1, a = 0.1 };

    ISEquippedItem.instance:addChild(ESR.Rankings.toolbarButton);
    ISEquippedItem.instance:setHeight(math.max(ISEquippedItem.instance:getHeight(), ESR.Rankings.toolbarButton:getY() + 400));
    prerenderEnabled = true

    if ESR.Rankings.Defaults.onMenuItemMouseDownMainMenu == nil then
        -- 检查是否需要加载战斗日志禁用模块
        if SandboxVars.UdderlyNoCombatLogging and pcall(require, "UdderlyNoCombatLogging/mod") then
            -- Module loaded successfully
        else
            ESR.Logger.error("OnCreatePlayer", { Message = "Failed to load UdderlyNoCombatLogging module" })
        end
        -- 替换主菜单项点击事
        ESR.Rankings.Defaults.onMenuItemMouseDownMainMenu = MainScreen.onMenuItemMouseDownMainMenu
        -- 替换主菜单项点击事件
        function MainScreen.onMenuItemMouseDownMainMenu(item, x, y)
            ESR.Rankings.SavePreferences()
            ESR.Rankings.Defaults.onMenuItemMouseDownMainMenu(item, x, y)
        end
    end

end

function ExaStockRanking.updateDataScoreboards()
    if (ESR.Rankings.window ~= nil) then
        ESR.Rankings.scoreboard:updateDataBase()
    end
end

function ExaStockRanking:updateDataScoreboard(scoreboard, data, max)
    if (ESR.Rankings.window ~= nil) then
        scoreboard:fullClear()
        scoreboard:totalEntries(max)
        for i, item in ipairs(data) do
            local currentItem = item
            scoreboard:addItem(currentItem.name, currentItem)
        end
    end
end

function ExaStockRanking:sortList(list)
    table.sort(list, function(a, b)
        if a.value == b.value then
            return a.name < b.name;
        else
            return a.value > b.value;
        end
    end)

    local username = getPlayer():getUsername()
    local tieCount = 0
    local max = SandboxVars.GSBQoL_Rankings.MaxPlayers
    local newList = {}
    local hiddenBefore = 0
    local hiddenAfter = 0
    local checkingBefore = true

    local playerData = nil

    local divisorFound = false

    for i, o in ipairs(list) do
        o["hasSpace"] = false
        if i > 1 then
            if o.value == list[i - 1].value then
                tieCount = tieCount + 1
            else
                if divisorFound then
                    o["hasSpace"] = true
                    divisorFound = false
                end
                tieCount = 0
            end

            if i == 3 then
                divisorFound = true
            end
        end
        -- o["indexToList"] = i - tieCount
        o["indexToList"] = i - (tieCount > 0 and 1 or 0)
        o["index"] = i
        if i - tieCount <= max then
        -- if i - (tieCount > 0 and 1 or 0) <= max then
            table.insert(newList, { name = o.name, value = o.value, indexToList = o.indexToList, index = o.index, hasSpace = o.hasSpace })
        else
            if o.name == username then
                playerData = { name = o.name, value = o.value, indexToList = o.indexToList, index = o.index, hasSpace = o.hasSpace, cut = true, hiddenBefore = hiddenBefore }
                table.insert(newList, playerData)
            elseif i == #list then
                if playerData then
                    if hiddenAfter > 0 then
                        playerData["hiddenAfter"] = hiddenAfter
                        playerData["cut"] = true
                    end
                end
                local last = { name = o.name, value = o.value, indexToList = o.indexToList, index = o.index, hasSpace = o.hasSpace }
                if hiddenBefore > 0 then
                    last["hiddenBefore"] = hiddenBefore
                    last["cut"] = true
                end
                table.insert(newList, last)
            else
                if checkingBefore then
                    hiddenBefore = hiddenBefore + 1
                else
                    hiddenAfter = hiddenAfter + 1
                end
            end
        end
    end

    return newList, #list
end

function ExaStockRanking:updateDataBase(arguments)
    local buyList = ESR.Rankings.data["buyList"] or {}
    local sellList = ESR.Rankings.data["sellList"] or {}
    local fundList = ESR.Rankings.data["fundList"] or {}

    local maxBuys = 0
    local maxSells = 0
    local maxFund = 0

    if arguments ~= nil then
        buyList = {}
        sellList = {}
        fundList = {}

        local currentPlayer = getPlayer()
        local currentUsername = currentPlayer:getUsername()

        if arguments ~= nil then
            for k, player in pairs(arguments) do
                if currentUsername == k then
                    ESR.Rankings.data["stock"] = player.stock or {}
                    ESR.Rankings.data["buys"] = player.buys or 0
                    ESR.Rankings.data["sells"] = player.sells or 0
                    ESR.Rankings.data["fund"] = player.fund or 0
                end
                if player ~= nil then
                    table.insert(buyList, { name = k, value = player.buys });
                    table.insert(sellList, { name = k, value = player.sells });
                    table.insert(fundList, { name = k, value = player.fund });
                end
            end
        end
    end

    buyList, maxBuys = self:sortList(buyList)
    sellList, maxSells = self:sortList(sellList)
    fundList, maxFund = self:sortList(fundList)

    self:updateDataScoreboard(self.scoreboardBuys, buyList, maxBuys)
    self:updateDataScoreboard(self.scoreboardSells, sellList, maxSells)
    self:updateDataScoreboard(self.scoreboardFund, fundList, maxFund)

    ESR.Rankings.data["buyList"] = buyList;
    ESR.Rankings.data["sellList"] = sellList;
    ESR.Rankings.data["fundList"] = fundList;
end


function ESR.Rankings.Commands.DataFromServer(arguments)
    if arguments ~= nil then
        ESR.Rankings.scoreboard:updateDataBase(arguments);
    else
        ESR.Rankings.scoreboard:updateDataBase({});
    end
end


function ESR.Rankings.Commands.PlayerPreferencesFromServer(arguments)
    if arguments then
        ESR.Rankings.Preferences.opened = arguments["opened"]
        ESR.Rankings.Preferences.x = arguments["x"]
        ESR.Rankings.Preferences.y = arguments["y"]

        if ESR.Rankings.Preferences.opened then
            ESR.Rankings.showWindow(getPlayer(), ESR.Rankings.Preferences.x, ESR.Rankings.Preferences.y)
        end
    end
end

function ESR.Rankings.Commands.DataToAdmin(arguments)
    ModData.remove(ESR.Rankings.Shared.MODDATA .. "Admin");
    ModData.add(ESR.Rankings.Shared.MODDATA .. "Admin", arguments);
    ESR.Rankings.Admin.data = arguments;
    ESR.Rankings.Admin.data["updated"] = getTimestamp() or os.time()

    --if ISGSBQoLRankingAdminWindow.instance ~= nil then
    --    ISGSBQoLRankingAdminWindow.instance:populateData();
    --end
end

function ESR.Rankings.RankingDataClient(module, command, player, arguments)
    if module == ESR.Rankings.Shared.MODULE_NAME then
        local rankings = ESR.Rankings;
        local windows = rankings.window;
        if (windows ~= nil or command == 'PlayerPreferencesFromServer') then
            if ESR.Rankings.Commands[command] then
                ESR.Rankings.Commands[command](arguments)
            else
                ESR.Logger.error("RankingDataClient", { Command = command, Message = "Invalid command" })
            end
        end
    end
end

function ESR.Rankings.EveryTenMinutesUpdateBaseCounts()
    local player = getPlayer()
    if player and player:isAlive() then
        local object = { player:getUsername(), player:getModData().buys,  player:getModData().sells, player:getModData().fund }
        sendClientCommand(player, ESR.Rankings.Shared.MODULE_NAME, "BaseInfo", object)
    end
end

function ESR.Rankings.OnPlayerDeath(_player, cause)
    if ESR.Rankings.window then
        ESR.Rankings.hideWindow(ESR.Rankings.window)
    end
    local player = getPlayer()
    local object = { player:getUsername(), player:getModData().buys, player:getModData().sells, player:getModData().fund }
    sendClientCommand(player, ESR.Rankings.Shared.MODULE_NAME, "BaseInfo", object)
end

-- Events.EveryTenMinutes.Add(ESR.Rankings.EveryTenMinutesUpdateBaseCounts);

Events.OnCreatePlayer.Add(ESR.Rankings.OnCreatePlayer);

Events.OnServerCommand.Add(ESR.Rankings.RankingDataClient);
Events.OnGameStart.Add(ESR.Rankings.OnGameStart);
